package db

import (
	"bytes"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"net/url"
	"os"
	"shop-common/library/config"
	"shop-common/library/env"
	"shop-common/library/variables"
	"strconv"
	"time"
)

var conn *gorm.DB

func NewMySQLWithGORM(mysqlSetting *config.Mysql) (*gorm.DB, error) {
	if mysqlSetting == nil {
		return nil, fmt.Errorf("mysqlSetting is nil")
	}
	if mysqlSetting.Username == "" {
		return nil, fmt.Errorf("lack of mysqlSetting.Username")
	}
	if mysqlSetting.Password == "" {
		return nil, fmt.Errorf("lack of mysqlSetting.Password")
	}
	if mysqlSetting.Host == "" {
		return nil, fmt.Errorf("lack of mysqlSetting.Host")
	}
	if mysqlSetting.DBName == "" {
		return nil, fmt.Errorf("lack of mysqlSetting.DBName")
	}
	if mysqlSetting.Charset == "" {
		return nil, fmt.Errorf("lack of mysqlSetting.Charset")
	}

	var buf bytes.Buffer
	buf.WriteString(mysqlSetting.Username)
	buf.WriteString(":")
	buf.WriteString(mysqlSetting.Password)
	buf.WriteString("@tcp(")
	buf.WriteString(mysqlSetting.Host)
	buf.WriteString(")/")
	buf.WriteString(mysqlSetting.DBName)
	buf.WriteString("?charset=")
	buf.WriteString(mysqlSetting.Charset)
	buf.WriteString("&parseTime=" + strconv.FormatBool(mysqlSetting.ParseTime))
	buf.WriteString("&multiStatements=" + strconv.FormatBool(mysqlSetting.MultiStatements))

	if mysqlSetting.Loc == "" {
		buf.WriteString("&loc=Local")
	} else {
		buf.WriteString("&loc=" + url.QueryEscape(mysqlSetting.Loc))
	}

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold:             time.Second, // Slow SQL threshold
			LogLevel:                  logger.Info, // Log level
			IgnoreRecordNotFoundError: true,        // Ignore ErrRecordNotFound error for logger
			Colorful:                  false,       // Disable color
		},
	)

	db, err := gorm.Open(mysql.New(mysql.Config{
		DriverName:                "mysql",
		DSN:                       buf.String(),
		Conn:                      nil,
		SkipInitializeWithVersion: false,
		DefaultStringSize:         256,
		DefaultDatetimePrecision:  nil,
		DisableDatetimePrecision:  true, // 禁用 datetime 精度，MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    false,
		DontSupportRenameColumn:   false,
		DontSupportForShareClause: false,
	}), &gorm.Config{
		Logger: newLogger,
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   TablePrefix, // 表名前缀
			SingularTable: true,        // 使用单数表名，启用该选项后
			NameReplacer:  nil,
			NoLowerCase:   false,
		},
		DisableForeignKeyConstraintWhenMigrating: true, // 禁用自动创建外键约束
		DisableAutomaticPing:                     true,
	})
	if err != nil {
		return nil, err
	}

	if env.IsDevMode() {
		//db.LogMode(true) // 设置为true之后控制台会输出对应的SQL语句
	}

	sqlDB, _ := db.DB()
	sqlDB.SetMaxOpenConns(mysqlSetting.MaxOpen) // 最大连接数
	sqlDB.SetMaxIdleConns(mysqlSetting.MaxIdle) // 最大空闲数

	conn = db
	return conn, nil
}

func DB() *gorm.DB {
	return conn
}

func NewTestGorm() *gorm.DB {
	dsn := "root:123456@(127.0.0.1:3306)/shop_go?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "tz_", // 表名前缀
			SingularTable: true,  // 使用单数表名，启用该选项后
			NameReplacer:  nil,
			NoLowerCase:   false,
		},
		DisableForeignKeyConstraintWhenMigrating: true,
		DisableAutomaticPing:                     true,
	})
	if err != nil {
		panic(err)
	}
	return db
}

const TablePrefix = "tz_"

func NewLocalTest() {
	_ = &config.Mysql{
		Host:            variables.MysqlSetting.Host,
		Username:        variables.MysqlSetting.Username,
		Password:        variables.MysqlSetting.Password,
		DBName:          variables.MysqlSetting.DBName,
		Charset:         variables.MysqlSetting.Charset,
		MaxIdle:         variables.MysqlSetting.MaxIdle,
		MaxOpen:         variables.MysqlSetting.MaxOpen,
		Loc:             variables.MysqlSetting.Loc,
		MultiStatements: variables.MysqlSetting.MultiStatements,
		ParseTime:       variables.MysqlSetting.ParseTime,
	}

	_ = &config.Redis{
		Addr:        variables.RedisSetting.Addr,
		Password:    variables.RedisSetting.Password,
		MaxIdle:     variables.RedisSetting.MaxIdle,
		MaxActive:   variables.RedisSetting.MaxActive,
		IdleTimeout: variables.RedisSetting.IdleTimeout,
		DB:          variables.RedisSetting.DB,
	}
}
